/*
	Copyright 2007-2010 Petr Ohlidal

	This file is a part of GrobMaker

	GrobMaker is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	GrobMaker is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with GrobMaker.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Image.hpp"
#include "../Exception.hpp"
#include "../Util/StringUtils.hpp"
#include <climits>
#include <cmath>

namespace GrobMaker{

void Image::save(const fs::path& path, FREE_IMAGE_FORMAT format, int flags){
	if( ::FreeImage_Save(format, this->pic, path.string().c_str(), flags) != TRUE ){
		throw Exception("Image::save(): Failed to save file");
	}
}

void Image::load( const fs::path& path, FREE_IMAGE_FORMAT format, int flags ){
	this->pic = ::FreeImage_Load(format, path.string().c_str(), flags);
	this->sourcePath = path;
	if( this->pic==NULL ){
		std::stringstream msg;
		msg<<"Image::load(): Failed to read file '"<<path<<"'";
		throw Exception( msg );
	}
}

Image::~Image(){
	this->unload();
}

bool Image::resolveExtension(std::string ext, FREE_IMAGE_FORMAT* format){
	ext = StringUtils::toLowerCase(ext);
	if( ext.compare("bmp")==0 ){
		if(format){
			*format = FIF_BMP;
		}
		return true;
	}
	else if( ext.compare("gif")==0 ){
		if(format){
			*format = FIF_GIF;
		}
		return true;
	}
	else if( ext.compare("jpg")==0 || ext.compare("jpeg")==0 || ext.compare("jpe")==0 ){
		if(format){
			*format = FIF_JPEG;
		}
		return true;
	}
	else if( ext.compare("png")==0 ){
		if(format){
			*format = FIF_PNG;
		}
		return true;
	}
	else if( ext.compare("tga")==0 || ext.compare("targa")==0 ){
		if(format){
			*format = FIF_TARGA;
		}
		return true;
	}
	else if( ext.compare("tif")==0 || ext.compare("tiff")==0 ){
		if(format){
			*format = FIF_TIFF;
		}
		return true;
	}
	else{
		return false;
	}
}

void Image::scale( int w, int h ){
	if( w==0 && h==0 ){
		return;
	}
	if(w!=0){
		h = static_cast<int>(
		( static_cast<float>( FreeImage_GetHeight(this->pic) )/
			static_cast<float>( FreeImage_GetWidth(this->pic) ) )*
				static_cast<float>(w) );
	}
	else{
		w = static_cast<int>( static_cast<float>(h)*
			(static_cast<float>(::FreeImage_GetWidth(this->pic))/static_cast<float>(::FreeImage_GetHeight(this->pic))) );
	}
	FIBITMAP* scaledPic = ::FreeImage_Rescale(this->pic, w, h, FILTER_LANCZOS3);
	if(scaledPic==NULL){
		throw Exception("Image::scale: operation failed");
	}
	::FreeImage_Unload(this->pic);
	this->pic = scaledPic;
}

bool Image::findAreaRect( Rectangle& area, const AreaFinder& finder ) const{
	int l=INT_MAX, r=INT_MIN, bt=INT_MIN, t=INT_MAX;
	int picWidth = static_cast<int>( ::FreeImage_GetWidth(this->pic) );
	int picHeight = static_cast<int>( ::FreeImage_GetHeight(this->pic) );
	int bytesPerPixel = static_cast<int>( ::FreeImage_GetBPP(this->pic)/8 );
	for( int scanline=0; scanline<picHeight; scanline++ ){
		BYTE* bytes = ::FreeImage_GetScanLine(this->pic, scanline);
		for( int pixel=0; pixel<picWidth; pixel++ ){
			if(	finder.check(bytes) ){
				if( pixel>r ){
					r = pixel;
				}
				if( pixel<l ){
					l = pixel;
				}
				if( scanline>bt ){
					bt = scanline;
				}
				if( scanline<t ){
					t = scanline;
				}
			}
			bytes+=bytesPerPixel;
		}
	}
	bool drawingFound = ( l!=INT_MAX && t!=INT_MAX );
	if(drawingFound){
		area.x = l;
		area.y = (picHeight-bt)-1;
		area.w = r-l;
		area.h = bt-t;
	}
	return drawingFound;
}


void Image::trim( Rectangle& area ){
	FIBITMAP* trimmedPic = ::FreeImage_Copy(this->pic, area.x, area.y, area.x+area.w, area.y+area.h);
	if(trimmedPic==NULL){
		throw Exception("Image::trim(): Operation failed");
	}
	::FreeImage_Unload(this->pic);
	this->pic = trimmedPic;
}

void Image::parseShadowSprite( Image& pic, float shadowAlpha ){
	FIBITMAP* img = pic.getBitmap();
	if( ::FreeImage_GetBPP(img)!=32u ){
		throw Exception("parseShadow(): Supplied image is not RGBA");
	}
	int height = static_cast<int>( ::FreeImage_GetHeight(img) );
	int width = static_cast<int>( ::FreeImage_GetWidth(img) );

	for( int scanline=0; scanline<height; scanline++ ){
		BYTE* bytes = ::FreeImage_GetScanLine(img, scanline);
		for	( int collumn=0; collumn<width; collumn++ ){

			if( bytes[FI_RGBA_RED]!=255 && bytes[FI_RGBA_GREEN]!=255 && bytes[FI_RGBA_BLUE]!=255 ){
				float srcWhite = static_cast<float>(bytes[FI_RGBA_RED]);
				float srcBlackPerc = static_cast<float>(std::fabs( srcWhite / 255.0f - 1.0 ));
				float resAlphaPerc = srcBlackPerc * shadowAlpha;
				bytes[FI_RGBA_ALPHA] = static_cast<unsigned char>(resAlphaPerc * 255.0f);
			}
			else{
				bytes[FI_RGBA_ALPHA] = 0;
			}
			bytes[FI_RGBA_RED] = 0;
			bytes[FI_RGBA_GREEN] = 0;
			bytes[FI_RGBA_BLUE] = 0;


			bytes+=4;
		}
	}
}

} // namespace GrobMaker
